TBB: add ECDSA support to the certificate generation tool
authorJuan Castillo <[email protected]>
Mon, 1 Jun 2015 15:34:23 +0000 (16:34 +0100)
committerJuan Castillo <[email protected]>
Thu, 25 Jun 2015 07:53:27 +0000 (08:53 +0100)
This patch extends the 'cert_create' tool to support ECDSA keys
to sign the certificates. The '--key-alg' command line option
can be used to specify the key algorithm when invoking the tool.
Available options are:

    * 'rsa': create RSA-2048 keys (default option)
    * 'ecdsa': create ECDSA-SECP256R1 keys

The TF Makefile has been updated to allow the platform to specify
the key algorithm by declaring the 'KEY_ALG' variable in the
platform makefile.

The behaviour regarding key management has changed. After applying
this patch, the tool will try first to open the keys from disk. If
one key does not exist or no key is specified, and the command line
option to create keys has been specified, new keys will be created.
Otherwise an error will be generated and the tool will exit. This
way, the user may specify certain keys while the tool will create
the remaining ones. This feature is useful for testing purposes
and CI infrastructures.

The OpenSSL directory may be specified using the build option
'OPENSSL_DIR' when building the certificate generation tool.
Default is '/usr'.

Change-Id: I98bcc2bfab28dd7179f17f1177ea7a65698df4e7

Makefile
tools/cert_create/Makefile
tools/cert_create/include/key.h
tools/cert_create/src/key.c
tools/cert_create/src/main.c

index 7f7ca8e73ff5434a1d8aee6b860176217513b090..faa6554a9f6786b0bb360d67a5075303502bb3dc 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -337,6 +337,7 @@ ifneq (${GENERATE_COT},0)
     $(eval CRT_ARGS += $(if ${TRUSTED_WORLD_KEY}, --trusted-world-key ${TRUSTED_WORLD_KEY}))
     $(eval CRT_ARGS += $(if ${NON_TRUSTED_WORLD_KEY}, --non-trusted-world-key ${NON_TRUSTED_WORLD_KEY}))
     $(eval CRT_ARGS += --trusted-key-cert ${TRUSTED_KEY_CERT})
+    $(eval CRT_ARGS += $(if ${KEY_ALG}, --key-alg ${KEY_ALG}))
 endif
 
 # Check Trusted Board Boot options
index eac8cec80aa10e1bb54d235a265c04f7ec3af674..a4bd76fb83ffd1a1cfc5cac9c98eeeedde493922 100644 (file)
@@ -33,6 +33,7 @@ PLAT          := none
 V              := 0
 DEBUG          := 0
 BINARY         := ${PROJECT}
+OPENSSL_DIR    := /usr
 
 OBJECTS := src/cert.o \
            src/ext.o \
@@ -69,8 +70,8 @@ endif
 
 # Make soft links and include from local directory otherwise wrong headers
 # could get pulled in from firmware tree.
-INC_DIR := -I ./include -I ${PLAT_INCLUDE}
-LIB_DIR :=
+INC_DIR := -I ./include -I ${PLAT_INCLUDE} -I ${OPENSSL_DIR}/include
+LIB_DIR := -L ${OPENSSL_DIR}/lib
 LIB := -lssl -lcrypto
 
 CC := gcc
index 88197500566971052a3becb3236dc0beecf69e28..dfb3150866ad968a07e072e0757569a03b8f4bd0 100644 (file)
 
 #define RSA_KEY_BITS           2048
 
+/* Error codes */
+enum {
+       KEY_ERR_NONE,
+       KEY_ERR_MALLOC,
+       KEY_ERR_FILENAME,
+       KEY_ERR_OPEN,
+       KEY_ERR_LOAD
+};
+
+/* Supported key algorithms */
+enum {
+       KEY_ALG_RSA,
+       KEY_ALG_ECDSA
+};
+
 /*
  * This structure contains the relevant information to create the keys
  * required to sign the certificates.
@@ -50,8 +65,8 @@ typedef struct key_s {
        EVP_PKEY *key;          /* Key container */
 } key_t;
 
-int key_new(key_t *key);
-int key_load(key_t *key);
+int key_create(key_t *key, int type);
+int key_load(key_t *key, unsigned int *err_code);
 int key_store(key_t *key);
 
 #endif /* KEY_H_ */
index b5737d93ef39a72078d00d05fde02d9bab168d2b..2137bf7d2e3b67bd52d46a9297dd67a3236c65ae 100644 (file)
 #define MAX_FILENAME_LEN               1024
 
 /*
- * Create a new key
+ * Create a new key container
  */
-int key_new(key_t *key)
+static int key_new(key_t *key)
 {
-       RSA *rsa = NULL;
-       EVP_PKEY *k = NULL;
-
        /* Create key pair container */
-       k = EVP_PKEY_new();
-       if (k == NULL) {
+       key->key = EVP_PKEY_new();
+       if (key->key == NULL) {
                return 0;
        }
 
-       /* Generate a new RSA key */
-       rsa = RSA_generate_key(RSA_KEY_BITS, RSA_F4, NULL, NULL);
-       if (EVP_PKEY_assign_RSA(k, rsa)) {
-               key->key = k;
-               return 1;
-       } else {
-               printf("Cannot assign RSA key\n");
+       return 1;
+}
+
+int key_create(key_t *key, int type)
+{
+       RSA *rsa = NULL;
+       EC_KEY *ec = NULL;
+
+       /* Create OpenSSL key container */
+       if (!key_new(key)) {
+               goto err;
        }
 
-       if (k)
-               EVP_PKEY_free(k);
+       switch (type) {
+       case KEY_ALG_RSA:
+               /* Generate a new RSA key */
+               rsa = RSA_generate_key(RSA_KEY_BITS, RSA_F4, NULL, NULL);
+               if (rsa == NULL) {
+                       printf("Cannot create RSA key\n");
+                       goto err;
+               }
+               if (!EVP_PKEY_assign_RSA(key->key, rsa)) {
+                       printf("Cannot assign RSA key\n");
+                       goto err;
+               }
+               break;
+       case KEY_ALG_ECDSA:
+               /* Generate a new ECDSA key */
+               ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+               if (ec == NULL) {
+                       printf("Cannot create EC key\n");
+                       goto err;
+               }
+               if (!EC_KEY_generate_key(ec)) {
+                       printf("Cannot generate EC key\n");
+                       goto err;
+               }
+               EC_KEY_set_flags(ec, EC_PKEY_NO_PARAMETERS);
+               EC_KEY_set_asn1_flag(ec, OPENSSL_EC_NAMED_CURVE);
+               if (!EVP_PKEY_assign_EC_KEY(key->key, ec)) {
+                       printf("Cannot assign EC key\n");
+                       goto err;
+               }
+               break;
+       default:
+               goto err;
+       }
+
+       return 1;
+
+err:
+       RSA_free(rsa);
+       EC_KEY_free(ec);
+
        return 0;
 }
 
-int key_load(key_t *key)
+int key_load(key_t *key, unsigned int *err_code)
 {
        FILE *fp = NULL;
        EVP_PKEY *k = NULL;
 
-       /* Create key pair container */
-       k = EVP_PKEY_new();
-       if (k == NULL) {
+       /* Create OpenSSL key container */
+       if (!key_new(key)) {
+               *err_code = KEY_ERR_MALLOC;
                return 0;
        }
 
@@ -88,24 +128,24 @@ int key_load(key_t *key)
                /* Load key from file */
                fp = fopen(key->fn, "r");
                if (fp) {
-                       k = PEM_read_PrivateKey(fp, &k, NULL, NULL);
+                       k = PEM_read_PrivateKey(fp, &key->key, NULL, NULL);
                        fclose(fp);
                        if (k) {
-                               key->key = k;
+                               *err_code = KEY_ERR_NONE;
                                return 1;
                        } else {
-                               ERROR("Cannot read key from %s\n", key->fn);
+                               ERROR("Cannot load key from %s\n", key->fn);
+                               *err_code = KEY_ERR_LOAD;
                        }
                } else {
-                       ERROR("Cannot open file %s\n", key->fn);
+                       WARN("Cannot open file %s\n", key->fn);
+                       *err_code = KEY_ERR_OPEN;
                }
        } else {
-               ERROR("Key filename not specified\n");
+               WARN("Key filename not specified\n");
+               *err_code = KEY_ERR_FILENAME;
        }
 
-       if (k)
-               EVP_PKEY_free(k);
-
        return 0;
 }
 
index 2af5247fc5e8bc52bd956b8adafe089efdcfad88..77faf42e13608c18e667c7147dd90e5bb49d7aca 100644 (file)
@@ -80,6 +80,7 @@
 #define VAL_DAYS                       7300
 #define ID_TO_BIT_MASK(id)             (1 << id)
 #define NVCOUNTER_VALUE                        0
+#define NUM_ELEM(x)                    ((sizeof(x)) / (sizeof(x[0])))
 
 /* Files */
 enum {
@@ -112,6 +113,7 @@ enum {
 };
 
 /* Global options */
+static int key_alg;
 static int new_keys;
 static int save_keys;
 static int print_cert;
@@ -138,6 +140,11 @@ static char *strdup(const char *str)
        return dup;
 }
 
+static const char *key_algs_str[] = {
+       [KEY_ALG_RSA] = "rsa",
+       [KEY_ALG_ECDSA] = "ecdsa"
+};
+
 /* Command line options */
 static const struct option long_opt[] = {
        /* Binary images */
@@ -166,6 +173,7 @@ static const struct option long_opt[] = {
        {"bl32-key", required_argument, 0, BL32_KEY_ID},
        {"bl33-key", required_argument, 0, BL33_KEY_ID},
        /* Common options */
+       {"key-alg", required_argument, 0, 'a'},
        {"help", no_argument, 0, 'h'},
        {"save-keys", no_argument, 0, 'k'},
        {"new-chain", no_argument, 0, 'n'},
@@ -189,6 +197,7 @@ static void print_help(const char *cmd)
                printf("        --%s <file>  \\\n", long_opt[i].name);
        }
        printf("\n");
+       printf("-a    Key algorithm: rsa (default), ecdsa\n");
        printf("-h    Print help and exit\n");
        printf("-k    Save key pairs into files. Filenames must be provided\n");
        printf("-n    Generate new key pairs if no key files are provided\n");
@@ -198,8 +207,27 @@ static void print_help(const char *cmd)
        exit(0);
 }
 
+static int get_key_alg(const char *key_alg_str)
+{
+       int i;
+
+       for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) {
+               if (0 == strcmp(key_alg_str, key_algs_str[i])) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
 static void check_cmd_params(void)
 {
+       /* Only save new keys */
+       if (save_keys && !new_keys) {
+               ERROR("Only new keys can be saved to disk\n");
+               exit(1);
+       }
+
        /* BL2, BL31 and BL33 are mandatory */
        if (certs[BL2_CERT].bin == NULL) {
                ERROR("BL2 image not specified\n");
@@ -276,15 +304,19 @@ int main(int argc, char *argv[])
        FILE *file = NULL;
        int i, tz_nvctr_nid, ntz_nvctr_nid, hash_nid, pk_nid;
        int c, opt_idx = 0;
+       unsigned int err_code;
        unsigned char md[SHA256_DIGEST_LENGTH];
        const EVP_MD *md_info;
 
        NOTICE("CoT Generation Tool: %s\n", build_msg);
        NOTICE("Target platform: %s\n", platform_msg);
 
+       /* Set default options */
+       key_alg = KEY_ALG_RSA;
+
        while (1) {
                /* getopt_long stores the option index here. */
-               c = getopt_long(argc, argv, "hknp", long_opt, &opt_idx);
+               c = getopt_long(argc, argv, "ahknp", long_opt, &opt_idx);
 
                /* Detect the end of the options. */
                if (c == -1) {
@@ -292,6 +324,13 @@ int main(int argc, char *argv[])
                }
 
                switch (c) {
+               case 'a':
+                       key_alg = get_key_alg(optarg);
+                       if (key_alg < 0) {
+                               ERROR("Invalid key algorithm '%s'\n", optarg);
+                               exit(1);
+                       }
+                       break;
                case 'h':
                        print_help(argv[0]);
                        break;
@@ -399,19 +438,41 @@ int main(int argc, char *argv[])
        CHECK_OID(ntz_nvctr_nid, NTZ_FW_NVCOUNTER_OID);
 
        /* Load private keys from files (or generate new ones) */
-       if (new_keys) {
-               for (i = 0 ; i < NUM_KEYS ; i++) {
-                       if (!key_new(&keys[i])) {
-                               ERROR("Error creating %s\n", keys[i].desc);
-                               exit(1);
-                       }
+       for (i = 0 ; i < NUM_KEYS ; i++) {
+               /* First try to load the key from disk */
+               if (key_load(&keys[i], &err_code)) {
+                       /* Key loaded successfully */
+                       continue;
                }
-       } else {
-               for (i = 0 ; i < NUM_KEYS ; i++) {
-                       if (!key_load(&keys[i])) {
-                               ERROR("Error loading %s\n", keys[i].desc);
+
+               /* Key not loaded. Check the error code */
+               if (err_code == KEY_ERR_MALLOC) {
+                       /* Cannot allocate memory. Abort. */
+                       ERROR("Malloc error while loading '%s'\n", keys[i].fn);
+                       exit(1);
+               } else if (err_code == KEY_ERR_LOAD) {
+                       /* File exists, but it does not contain a valid private
+                        * key. Abort. */
+                       ERROR("Error loading '%s'\n", keys[i].fn);
+                       exit(1);
+               }
+
+               /* File does not exist, could not be opened or no filename was
+                * given */
+               if (new_keys) {
+                       /* Try to create a new key */
+                       NOTICE("Creating new key for '%s'\n", keys[i].desc);
+                       if (!key_create(&keys[i], key_alg)) {
+                               ERROR("Error creating key '%s'\n", keys[i].desc);
                                exit(1);
                        }
+               } else {
+                       if (err_code == KEY_ERR_OPEN) {
+                               ERROR("Error opening '%s'\n", keys[i].fn);
+                       } else {
+                               ERROR("Key '%s' not specified\n", keys[i].desc);
+                       }
+                       exit(1);
                }
        }